#
# Matcher.rb - A brute force matcher which applies the automaton to text.
#

class Matcher

  # Initializes the instance with the automaton arrays (ic, state1, state2) and
  # which state the matching should starting from.
  def initialize(ic, state1, state2, first_state)
    @num_match = 0
    @ic = ic           
    @state1 = state1   
    @state2 = state2
    @inizio = []
    @fine = []
    @first_state = first_state
  end
  
  attr_reader :num_match, :inizio, :fine
  attr_writer :verbose, :debug

  # Begins the brute force scanning of the text. Will call n times ricerca(), 
  # where n is the length of the string contained in the 'text' parameter.
  def ricerca_scan(text)
    matches = 0
    
    for i in 0 .. text.length-1
      matches += ricerca(text, i) 
    end  
    
    return matches
  end
  
  # Scans the given text from the given index. Is called by ricerca_scan().
  def ricerca(text, index)
    q = @ic.size   
    # read = simbolo di separazione tra stati compatibili con text[0..j-1] e
    # stati compatibili con text[0..j]
    read = -1    
    # Dichiarazione di una variabile da utilizzare come coda, in cui poter
    # inserita testa e in coda ed estrarre in coda
    coda = []     
    n = text.size
    #for i in (0..n-1) do
    #j = i
    j = index
    s = @first_state
    coda.push(read)
    
    if @debug
      print "[DEBUG] Initialization values : q = " + q.to_s + ", index = " + index.to_s
      print ", first_state = " + @first_state.to_s + "\n"
    end
    
    step = 0
    
    while s <= q - 1
      # Se s=read allora sono stati analizzati tutti gli stati compatibili con
      # text[0..j-1], perciò si legge un altro carattere
      step += 1
      
      if @debug
        print "[DEBUG] Step " + step.to_s + " -> state = " + s.to_s + "\n"
        print "[DEBUG] Queue content : " + coda.to_s + "\n"
        print "[DEBUG] Current character : " + j.to_s + "\n"
      end
      
      if s == read
        if @debug
          print "[DEBUG]   no more states, increasing character index\n"
        end
        
        j = j + 1
        coda.push(read)  # rimetto il -1 alla fine della coda
      elsif @ic[s] == text[j]   # stato deterministico
        if @debug
          print "[DEBUG]   DET pushing state " + @state1[s].to_s + "\n"
        end
        
        coda.push(@state1[s])
      elsif @ic[s] == '#'       # stato non deterministico
        # Simuliamo il comportamento di push (inserire in testa) con metodi della
        # libreria del Ruby
        if @debug
          print "[DEBUG]   INDET pushing states " + @state1[s].to_s + ", " + @state2[s].to_s + "\n"
        end
        
        coda.insert(0, @state1[s])
        
        # Aggiungo state2 solo se e' diverso da state1! (evito stati indet. non necessari)
        if (@state1[s] != @state2[s]) 
          coda.insert(0, @state2[s])
        end
      else
        if @debug
          print "[DEBUG]   no match\n"
        end
      end

      if coda.empty? or j >=  n + 1
        if @debug
          print "[DEBUG]   Reached end of queue\n"
        end
        
        return 0  # qua bisogna uscire di violenza o cicla all'infinito...
      else
        # Simuliamo il comportamento del pop (estrazione dalla testa) con metodi 
        # della libreria del Ruby
        s = coda.reverse!.pop
        coda.reverse!
      end
    end
    
    if @verbose
      #bisognerà poi vedere come tenere traccia del punto di match, 
      print "Match found from " + index.to_s +  " to "+ j.to_s + "\n"
    end

    @inizio << index
    @fine << j
    @num_match += 1
    return @num_match          #ma dipende da come si realizza il ciclo..
    #end
  end
end